/*
   Copyright (c) [2021] IT.SOS
   wxsdk is licensed under Mulan PSL v2.
   You can use this software according to the terms and conditions of the Mulan PSL v2.
   You may obtain a copy of Mulan PSL v2 at:
            http://license.coscl.org.cn/MulanPSL2
   THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
   See the Mulan PSL v2 for more details.
*/

package wxchat

import (
	"crypto/sha1"
	"encoding/json"
	"fmt"
	log2 "log"
	"strconv"
	"strings"
	"sync"
	"time"

	"gitee.com/itsos/wxsdk/common/config"
	"github.com/it-sos/golibs/utils/random"
)

type jsapiTicketDTO struct {
	ErrCode   int    `json:"errcode"`
	ErrMsg    string `json:"errmsg"`
	ExpiresIn int    `json:"expires_in"`
	Ticket    string `json:"ticket"`
}

type JsSdkDTO struct {
	AppId     string `json:"appid"`
	Noncestr  string `json:"noncestr"`
	Timestamp string `json:"timestamp"`
	Signature string `json:"signature"`
}

type JsapiTicket interface {
	getJsapiTicket() string
	getKey() string
	getLockKey() string
	getSign(url string, dto JsSdkDTO) string
	GetJsSdk(url string) JsSdkDTO
}

type jsapiTicket struct{}

func (j jsapiTicket) getSign(url string, dto JsSdkDTO) string {
	urlSlice := strings.Split(url, "#")
	h := sha1.New()
	params := fmt.Sprintf("jsapi_ticket=%s&noncestr=%s&timestamp=%s&url=%s",
		j.getJsapiTicket(),
		dto.Noncestr,
		dto.Timestamp,
		urlSlice[0])
	if config.Cfg.GetWxDebug() {
		log2.Default().Println(params)
	}
	h.Write([]byte(params))
	return fmt.Sprintf("%x", h.Sum(nil))
}

func (j jsapiTicket) GetJsSdk(url string) JsSdkDTO {
	jssdk := JsSdkDTO{
		AppId:     config.Cfg.GetWxAppId(),
		Noncestr:  random.Rand(16, random.RandMix),
		Timestamp: strconv.FormatInt(time.Now().UTC().Unix(), 10),
	}
	jssdk.Signature = j.getSign(url, jssdk)
	return jssdk
}

func (j jsapiTicket) getKey() string {
	return "jsapi_ticket:" + config.Cfg.GetWxAppId()
}

func (j jsapiTicket) getLockKey() string {
	return "jsapi_ticket_lock:" + config.Cfg.GetWxAppId()
}

func (j jsapiTicket) getJsapiTicket() string {
	var http = newHttp()

	// 7200 - 200
	http.setExpire(7000)
	http.setKey(j.getKey())
	http.setLockKey(j.getLockKey())

	resp := http.get(strings.NewReplacer("{access_token}", NewAccessToken().getAccessToken()).Replace(getJsapiTicket))
	dto := &jsapiTicketDTO{}
	if err := json.Unmarshal(resp, dto); err != nil {
		panic(err)
	}
	return dto.Ticket
}

var _ JsapiTicket = (*jsapiTicket)(nil)

var instJsapiTicket JsapiTicket
var onceJsapiTicket sync.Once

func NewJsapiTicket() JsapiTicket {
	onceJsapiTicket.Do(func() {
		instJsapiTicket = new(jsapiTicket)
	})
	return instJsapiTicket
}
